package tests;

import static org.junit.Assert.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import Exceptions.XMLRidesParserException;
import Stubs.CentralComputerStub3;

import sourceFiles.ExtremeRideEntry;
import sourceFiles.IRide;
import sourceFiles.RegularRideEntry;
import sourceFiles.Ride;
import sourceFiles.RideEntry;
import sourceFiles.RideState;
import sourceFiles.XMLRidesParser;

/**
 * Tests the XMLRidesParser unit
 * 
 * @author LASO-Team
 * 
 */
public class XMLRidesParserTest {
	static private String masterXMLFilePath;
	static private String copyXMLFilePath;
	private static final String IN_PROJECT_PATH_TO_TESTS = "\\src\\tests";

	/**
	 * Sets up the test to write the resulting xml data in a new file
	 * 
	 * @throws Exception
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		String currentPath = new java.io.File(".").getCanonicalPath();
		currentPath = currentPath + IN_PROJECT_PATH_TO_TESTS;
		masterXMLFilePath = currentPath + "\\TestsMaster.xml";
		copyXMLFilePath = currentPath
				+ "\\results\\ResultsUnitXMLRidesParser.xml";
	}

	/**
	 * Coping the master file to the new file location
	 * 
	 * @throws IOException
	 */
	@Before
	public void setUp() throws IOException {
		copySourceFile();
	}

	private void copySourceFile() throws FileNotFoundException, IOException {
		InputStream inStream = null;
		OutputStream outStream = null;

		File afile = new File(masterXMLFilePath);
		File bfile = new File(copyXMLFilePath);

		inStream = new FileInputStream(afile);
		outStream = new FileOutputStream(bfile);

		byte[] buffer = new byte[1024];

		int length;
		// copy the file content in bytes
		while ((length = inStream.read(buffer)) > 0) {

			outStream.write(buffer, 0, length);
		}
		inStream.close();
		outStream.close();
	}

	/**
	 * Tests that the constructor for the XMLRidesParser acts as expected
	 * 
	 * @throws XMLRidesParserException
	 */
	@Test
	public void testXMLRidesParserCtor() throws XMLRidesParserException {
		try {
			XMLRidesParser p = new XMLRidesParser(null);
			fail("Tried to open a non-existing file");
		} catch (XMLRidesParserException e) {
		}
		try {
			XMLRidesParser p = new XMLRidesParser("aaa");
			fail("Tried to open a non-existing file");
		} catch (XMLRidesParserException e) {
		}
		XMLRidesParser p = new XMLRidesParser(copyXMLFilePath);
	}

	/**
	 * Reading the Rides data from the copied xml file and making sure the
	 * results are consistent with the copied file
	 * 
	 * @throws XMLRidesParserException
	 */
	@Test
	public void testReadRidesFromFile() throws XMLRidesParserException {
		XMLRidesParser p = new XMLRidesParser(copyXMLFilePath);
		LinkedList<IRide> ridesList = p
				.readRidesFromFile(new CentralComputerStub3());
		assertTrue("Unexpacted number of Rides read from Tests.xml",
				ridesList.size() == 4);

		for (IRide ride : ridesList) {
			switch (ride.getRideID()) {
			case 1:
				assertTrue(ride.getMaxCapacity() == 100);
				assertTrue(ride.getMinimumAge() == 15);
				assertTrue(ride.getMinimumHeight() == 160);
				assertTrue(ride.getRideName().equals("Anaconda"));
				break;
			case 2:
				assertTrue(ride.getMaxCapacity() == 20);
				assertTrue(ride.getMinimumAge() == 8);
				assertTrue(ride.getMinimumHeight() == 130);
				assertTrue(ride.getRideName().equals("BumpingCars"));
				break;
			case 3:
				assertTrue(ride.getMaxCapacity() == 100);
				assertTrue(ride.getMinimumAge() == 8);
				assertTrue(ride.getMinimumHeight() == 100);
				assertTrue(ride.getRideName().equals("FerrisWheel"));
				break;
			case 4:
				assertTrue(ride.getMaxCapacity() == 15);
				assertTrue(ride.getMinimumAge() == 12);
				assertTrue(ride.getMinimumHeight() == 100);
				assertTrue(ride.getRideName().equals("HorrorHouse!"));
				break;
			default:
				fail("Unexpected Ride id found in xml file");
			}
		}
	}

	/**
	 * Reading the RideEntries data from the copied xml file and making sure the
	 * results are consistent with the copied file
	 * 
	 * @throws XMLRidesParserException
	 */
	@Test
	public void testReadRideEntriesFromFile() throws XMLRidesParserException {
		XMLRidesParser p = new XMLRidesParser(copyXMLFilePath);
		LinkedList<RideEntry> ridesEntriesList = p.readRideEntriesFromFile();
		assertTrue("Unexpacted number of Rides read from Tests.xml",
				ridesEntriesList.size() == 4);

		for (RideEntry ride : ridesEntriesList) {
			switch (ride.getRideID()) {
			case 1:
				assertTrue(ride.getRideName().equals("Anaconda"));
				assertTrue(ride.getPrice() == 15.0);
				assertTrue(ride.isExtreme()); // isExtreme=TRUE
				break;
			case 2:
				assertTrue(ride.getRideName().equals("BumpingCars"));
				assertTrue(ride.getPrice() == 10.0);
				assertTrue(!(ride.isExtreme())); // isExtreme=FALSE
				break;
			case 3:
				assertTrue(ride.getRideName().equals("FerrisWheel"));
				assertTrue(ride.getPrice() == 10.0);
				assertTrue(!(ride.isExtreme())); // isExtreme=FALSE
				break;
			case 4:
				assertTrue(ride.getRideName().equals("HorrorHouse!"));
				assertTrue(ride.getPrice() == 15.0);
				assertTrue(ride.isExtreme()); // isExtreme=TRUE
				break;
			default:
				fail("Unexpected Ride id found in xml file");
			}
		}
	}

	/**
	 * testing that new rides and rides entries can be written correctly to the
	 * file
	 * 
	 * @throws XMLRidesParserException
	 */
	@Test
	public void testWriteNewRideDataToFile() throws XMLRidesParserException {
		XMLRidesParser p = new XMLRidesParser(copyXMLFilePath);

		RideEntry newEntry = new ExtremeRideEntry("RollerCoaster", 111111, 4.0);
		IRide newRide = new Ride(111111, 30, 14, 160, "RollerCoaster",
				new CentralComputerStub3());
		p.writeNewRideDataToFile(newRide, newEntry);

		RideEntry newEntry1 = new RegularRideEntry("RollerCoasterForKids",
				111112, 5.0);
		IRide newRide1 = new Ride(11112, 30, 14, 160, "RollerCoasterForKids",
				new CentralComputerStub3());
		p.writeNewRideDataToFile(newRide1, newEntry1);

		LinkedList<IRide> rideList = p
				.readRidesFromFile(new CentralComputerStub3());
		LinkedList<RideEntry> rideEntryList = p.readRideEntriesFromFile();
		assertTrue(rideList.size() == 6 && rideEntryList.size() == 6);
		for (RideEntry rideEntry : rideEntryList) {
			switch (rideEntry.getRideID()) {
			case 111111:
				assertTrue(rideEntry.getRideName().equals("RollerCoaster"));
				assertTrue(rideEntry.getPrice() == 4.0);
				assertTrue(rideEntry.isExtreme()); // isExtreme=TRUE
				break;
			case 111112:
				assertTrue(rideEntry.getRideName().equals(
						"RollerCoasterForKids"));
				assertTrue(rideEntry.getPrice() == 5.0);
				assertTrue(!(rideEntry.isExtreme())); // isExtreme=FALSE
			default:
				;
			}
		}
		for (IRide ride : rideList) {
			switch (ride.getRideID()) {
			case 111111:
				assertTrue(ride.getMaxCapacity() == 30);
				assertTrue(ride.getMinimumAge() == 14);
				assertTrue(ride.getMinimumHeight() == 160);
				assertTrue(ride.getRideName().equals("RollerCoaster"));
				break;
			case 111112:
				assertTrue(ride.getMaxCapacity() == 30);
				assertTrue(ride.getMinimumAge() == 14);
				assertTrue(ride.getMinimumHeight() == 160);
				assertTrue(ride.getRideName().equals("RollerCoasterForKids"));
			default:
				;
			}
		}
	}

	/**
	 * testing that updating an existing ride can be done successfully
	 * 
	 * @throws XMLRidesParserException
	 */
	@Test
	public void testSuccessfulUpdateRideDataInFile()
			throws XMLRidesParserException {
		XMLRidesParser p = new XMLRidesParser(copyXMLFilePath);

		// first create an update ride version (both Ride and RideEntry) with
		// rideID that appears in the XML file
		RideEntry entry1 = new RegularRideEntry("RollerCoasterForKids", 1, 5.0);
		IRide ride1 = new Ride(1, 30, 14, 160, "RollerCoasterForKids",
				new CentralComputerStub3());

		// try and update the file using the above data (should succeed)
		p.updateRideDataInFile(ride1, entry1);

		// get the new Rides list and the new RideEntries list after update
		LinkedList<IRide> rideList = p
				.readRidesFromFile(new CentralComputerStub3());
		LinkedList<RideEntry> rideEntryList = p.readRideEntriesFromFile();

		// lists size should'nt have stayed 4
		assertTrue(rideList.size() == 4 && rideEntryList.size() == 4);

		// find the updated ride and test whether it's data is consistent with
		// update
		for (RideEntry rideEntry : rideEntryList) {
			switch (rideEntry.getRideID()) {
			case 1:
				assertTrue(rideEntry.getRideName().equals(
						"RollerCoasterForKids"));
				assertTrue(rideEntry.getPrice() == 5.0);
				assertTrue(!(rideEntry.isExtreme())); // isExtreme=TRUE
			default:
				;
			}
		}
		// find the updated rideEntry and test whether it's data is consistent
		// with update
		for (IRide ride : rideList) {
			switch (ride.getRideID()) {
			case 1:
				assertTrue(ride.getMaxCapacity() == 30);
				assertTrue(ride.getMinimumAge() == 14);
				assertTrue(ride.getMinimumHeight() == 160);
				assertTrue(ride.getRideName().equals("RollerCoasterForKids"));
			default:
				;
			}
		}
	}

	/**
	 * testing that an unsuccessful update doesn't affect the xml file
	 * 
	 * @throws XMLRidesParserException
	 */
	@Test
	public void testUnsuccessfulUpdateRideDataInFile()
			throws XMLRidesParserException {
		XMLRidesParser p = new XMLRidesParser(copyXMLFilePath);

		// first create an update ride version (both Ride and RideEntry) with
		// rideID that doesn't appears in the XML file
		RideEntry entry1 = new RegularRideEntry("RollerCoasterForKids", 111111,
				5.0);
		IRide ride1 = new Ride(111111, 30, 14, 160, "RollerCoasterForKids",
				new CentralComputerStub3());
		// try and update the file using the above data (should fail and throw
		// XMLRidesParserException)
		try {
			p.updateRideDataInFile(ride1, entry1);
			fail("Tried to update a non-existing ride here");
		} catch (XMLRidesParserException e) {

		}
	}

}
